// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <gtest/gtest.h>
#include <climits>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <cantor/parser/parser.h>
#include <cantor/utility/json_reader.h>
#include <tests/config.h>
#include <tests/paser/node_check.h>
#include <turbo/flags/flag.h>

TURBO_FLAG(bool, show_json, false, "show json in test");

int main(int argc, char *argv[]) {
    testing::InitGoogleTest(&argc, argv);
    return RUN_ALL_TESTS();
}

namespace cantor::parser {
    class DataJson {
    public:
        static DataJson &instance() {
            static DataJson instance;
            return instance;
        }

        const nlohmann::ordered_json &json() const { return json_; }

    private:
        DataJson() {
            std::string json_path = turbo::str_cat(testing::test_root, "/data/insert_list.json");
            auto rs = read_file(json_path);
            KCHECK(rs.ok());
            json_ = std::move(rs).value_or_die();
        }

        nlohmann::ordered_json json_;
    };

    struct ScopeTester {
        ScopeTester(const std::string &group, const std::string &case_str, bool show = turbo::get_flag(FLAGS_show_json))
            : group(group), case_str(case_str), show_json(show) {
            auto ins = DataJson::instance().json();
            auto json_group = ins[group].get<nlohmann::json>();
            sql = json_group[case_str].get<std::string>();
            KLOG(INFO) << NOPREFIX << "testing [" << group << ":" << case_str << "] sql<<<" << sql << ">>>";
        }

        ~ScopeTester() {
            KLOG(INFO) << NOPREFIX << "testing done..........";
        }

        std::string group;
        std::string case_str;
        bool show_json;
        std::string sql;
    };

    TEST(test_parser, case_insert) {
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert0");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            std::cout << "location:" << insert_stmt->location.location << " len:" << insert_stmt->location.len <<
                    std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(2, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            cantor::parser::Assignment *assign1 = insert_stmt->on_duplicate[1];

            ASSERT_TRUE(assign1->name->table_name.to_string() == "table_b");
            ASSERT_TRUE(assign1->name->column_name.to_string() == "field_b");
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert1");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            cantor::parser::InsertStmt *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_DELAYED_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.empty());
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(2, insert_stmt->columns.size());

            ASSERT_TRUE(insert_stmt->columns[0]->table_name.to_string() == "table_a");
            ASSERT_TRUE(insert_stmt->columns[0]->column_name.to_string() == "field_a");

            ASSERT_TRUE(insert_stmt->columns[1]->column_name.to_string() == "field_b");

            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(2, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);

            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            cantor::parser::Assignment *assign1 = insert_stmt->on_duplicate[1];

            ASSERT_TRUE(assign1->name->table_name.to_string() == "table_b");
            ASSERT_TRUE(assign1->name->column_name.to_string() == "field_b");
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert2");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_DELAYED_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.empty());
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(1, insert_stmt->columns.size());
            ASSERT_TRUE(insert_stmt->columns[0]->db_name.to_string() == "db");
            ASSERT_TRUE(insert_stmt->columns[0]->table_name.to_string() == "table_a");
            ASSERT_TRUE(insert_stmt->columns[0]->column_name.to_string() == "field_a");

            ASSERT_EQ(1, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());

            ASSERT_EQ(2, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);
            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            cantor::parser::Assignment *assign1 = insert_stmt->on_duplicate[1];

            ASSERT_TRUE(assign1->name->table_name.to_string() == "table_b");
            ASSERT_TRUE(assign1->name->column_name.to_string() == "field_b");
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert3");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_HIGH_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.empty());
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());

            ASSERT_EQ(2, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());

            ASSERT_EQ(2, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);

            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            cantor::parser::Assignment *assign1 = insert_stmt->on_duplicate[1];
            ASSERT_TRUE(assign1->name->table_name.to_string() == "table_b");
            ASSERT_TRUE(assign1->name->column_name.to_string() == "field_b");
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert4");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_HIGH_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.empty());
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());

            ASSERT_EQ(2, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(1, row_expr0->sub_exprs.size());
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(1, row_expr1->sub_exprs.size());

            ASSERT_EQ(2, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);
            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);

            cantor::parser::Assignment *assign1 = insert_stmt->on_duplicate[1];
            ASSERT_TRUE(assign1->name->table_name.to_string() == "table_b");
            ASSERT_TRUE(assign1->name->column_name.to_string() == "field_b");
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert5");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_HIGH_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.empty());
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());

            ASSERT_EQ(2, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(1, row_expr0->sub_exprs.size());
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(1, row_expr1->sub_exprs.size());

            ASSERT_EQ(0, insert_stmt->on_duplicate.size());
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert6");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            cantor::parser::InsertStmt *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_HIGH_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.empty());
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());

            ASSERT_EQ(1, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(1, row_expr0->sub_exprs.size());

            ASSERT_EQ(0, insert_stmt->on_duplicate.size());
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert7");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            std::cout << "location:" << insert_stmt->location.location << " len:" << insert_stmt->location.len <<
                    std::endl;
            std::cout << "location:" << ((RowExpr *) insert_stmt->lists[0])->location.location << " len:" << ((RowExpr
                *) insert_stmt->lists[0])->location.len << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.to_string() == "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(2, insert_stmt->columns.size());

            ASSERT_TRUE(insert_stmt->columns[0]->column_name.to_string() == "field_a");
            std::cout << "1111111111111111111111111111" << std::endl;

            ASSERT_TRUE(insert_stmt->columns[1]->column_name.to_string() == "field_b");

            ASSERT_EQ(1, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());

            ASSERT_EQ(2, insert_stmt->on_duplicate.size());
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert8");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.to_string() == "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(1, insert_stmt->columns.size());

            ASSERT_TRUE(insert_stmt->columns[0]->column_name.to_string() == "field_a");

            ASSERT_EQ(1, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(1, row_expr0->sub_exprs.size());

            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert9");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.to_string() == "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "t1");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_TRUE(insert_stmt->subquery_stmt != nullptr);
            ASSERT_TRUE(insert_stmt->subquery_stmt->stmt_type == pb::ast::STMT_SELECT);
            auto *select_stmt = (cantor::parser::SelectStmt *) insert_stmt->subquery_stmt;
            ASSERT_EQ(3, select_stmt->fields.size());
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert10");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.to_string() == "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "t1");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_TRUE(insert_stmt->subquery_stmt != nullptr);
            ASSERT_TRUE(insert_stmt->subquery_stmt->stmt_type == pb::ast::STMT_SELECT);
            auto *select_stmt = (cantor::parser::SelectStmt *) insert_stmt->subquery_stmt;
            ASSERT_EQ(3, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->is_in_braces == true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_insert", "sql_insert11");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.to_string() == "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "student1");
            ASSERT_EQ(9, insert_stmt->columns.size());
            ASSERT_TRUE(insert_stmt->subquery_stmt != nullptr);
            ASSERT_TRUE(insert_stmt->subquery_stmt->stmt_type == pb::ast::STMT_SELECT);
            auto *select_stmt = (cantor::parser::SelectStmt *) insert_stmt->subquery_stmt;
            ASSERT_EQ(9, select_stmt->fields.size());
            ASSERT_TRUE(select_stmt->is_in_braces == true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_merge) {
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_merge", "sql_merge_insert0");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error);
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_FALSE(insert_stmt->is_ignore);
            ASSERT_TRUE(insert_stmt->is_merge);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);
            ASSERT_TRUE(insert_stmt->table_name->db_name.to_string() == "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(1, insert_stmt->columns.size());

            ASSERT_TRUE(insert_stmt->columns[0]->column_name.to_string() == "field_a");

            ASSERT_EQ(1, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(1, row_expr0->sub_exprs.size());

            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
    }

    TEST(test_parser, case_construct) {
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert0");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_ARRAY_CTOR_FUNC);
            auto func0 = (CtorArrayFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.values.size(), 3);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert1");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_STRUCT_CTOR_FUNC);
            auto func0 = (CtorStructFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.values.size(), 2);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert2");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            cantor::parser::InsertStmt *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_MAP_CTOR_FUNC);
            auto func0 = (CtorMapFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.values.size(), 2);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert3");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_SET_CTOR_FUNC);
            auto func0 = (CtorSetFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.values.size(), 2);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert4");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_TUPLE_CTOR_FUNC);
            auto func0 = (CtorTupleFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.values.size(), 3);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert5");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_UNION_CTOR_FUNC);
            auto func0 = (CtorUnionFuncExpr *) sub_expr0;
            ASSERT_NE(func0->arg->value, nullptr);
            ASSERT_EQ(func0->arg->expr_type, pb::ast::ENT_DICT_PAIR);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert6");
            parser.parse(scoper.sql);
            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_PROTO_CTOR_FUNC);
            auto func0 = (CtorProtoFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.values.size(), 2);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert7");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_JSON_CTOR_FUNC);
            auto func0 = (CtorJsonFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.values.size(), 2);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert8", true);
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            cantor::parser::InsertStmt *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_JSON_ARRAY_CTOR_FUNC);
            auto func0 = (CtorJsonArrayFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.values.size(), 3);
            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert9");
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            std::cout << insert_stmt->to_string() << std::endl;
            ASSERT_FALSE(insert_stmt->is_replace);
            ASSERT_TRUE(insert_stmt->is_ignore);
            ASSERT_EQ(insert_stmt->priority, cantor::pb::ast::PE_LOW_PRIORITY);
            ASSERT_TRUE(insert_stmt->table_name != nullptr);

            ASSERT_EQ(insert_stmt->table_name->db_name.to_string(), "db");
            ASSERT_EQ(insert_stmt->table_name->table_name.to_string(), "table_a");
            ASSERT_EQ(0, insert_stmt->columns.size());
            ASSERT_EQ(3, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(2, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_ENUM_CTOR_FUNC);
            auto func0 = (CtorEnumFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.is_field, true);
            ASSERT_EQ(func0->arg.value, -1);
            ASSERT_EQ(func0->arg.field.to_string(), "a");

            cantor::parser::RowExpr *row_expr1 = insert_stmt->lists[1];
            ASSERT_EQ(2, row_expr1->sub_exprs.size());
            cantor::parser::RowExpr *row_expr2 = insert_stmt->lists[2];
            ASSERT_EQ(2, row_expr2->sub_exprs.size());
            /// check (enum db.tbl.level {1}, 'test') segment
            auto sub_expr2 = row_expr2->sub_exprs[0];
            ASSERT_EQ(sub_expr2->expr_type, cantor::pb::ast::ENT_ENUM_CTOR_FUNC);
            auto func2 = (CtorEnumFuncExpr *) sub_expr2;
            ASSERT_EQ(func2->arg.is_field, false);
            ASSERT_EQ(func2->arg.value, 1);
            ASSERT_EQ(func2->arg.field.empty(), true);
            ASSERT_NE(func2->arg.uri, nullptr);
            ASSERT_EQ(func2->arg.uri->ns_name.to_string(), "ns");
            ASSERT_EQ(func2->arg.uri->db_name.to_string(), "db");
            ASSERT_EQ(func2->arg.uri->table_name.to_string(), "");
            ASSERT_EQ(func2->arg.uri->type_name.to_string(), "level");

            ASSERT_EQ(1, insert_stmt->on_duplicate.size());
            cantor::parser::Assignment *assign0 = insert_stmt->on_duplicate[0];
            ASSERT_TRUE(assign0->name != nullptr);


            ASSERT_TRUE(assign0->name->db_name.to_string() == "db");
            ASSERT_TRUE(assign0->name->table_name.to_string() == "table_a");
            ASSERT_TRUE(assign0->name->column_name.to_string() == "field_a");
            ASSERT_TRUE(assign0->expr != nullptr);
            turbo::set_flag(&FLAGS_ast_print_sample, true);
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert10", true);
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        ///
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert11", true);
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert12", true);
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
            ASSERT_EQ(1, insert_stmt->lists.size());
            cantor::parser::RowExpr *row_expr0 = insert_stmt->lists[0];
            ASSERT_EQ(3, row_expr0->sub_exprs.size());
            auto sub_expr0 = row_expr0->sub_exprs[0];
            ASSERT_EQ(sub_expr0->expr_type, cantor::pb::ast::ENT_UDF_CTOR_FUNC);
            auto func0 = (CtorUdfFuncExpr *) sub_expr0;
            ASSERT_EQ(func0->arg.arg_type, cantor::pb::ast::ENT_EXPR_LIST_ARG);
            ASSERT_EQ(func0->arg.arg->expr_type, cantor::pb::ast::ENT_EXPR_LIST_ARG);
            auto arg = (ExprListArg*)func0->arg.arg;
            ASSERT_EQ(arg->values.size(), 3);
        }
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert12", true);
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
        {
            cantor::parser::SqlParser parser;
            ScopeTester scoper("case_construct", "sql_construct_insert14", true);
            parser.parse(scoper.sql);

            ASSERT_EQ(0, parser.error) << parser.syntax_err_str;
            ASSERT_EQ(1, parser.result.size());
            ASSERT_TRUE(typeid(*(parser.result[0])) == typeid(cantor::parser::InsertStmt));
            auto *insert_stmt = (cantor::parser::InsertStmt *) parser.result[0];
            if (scoper.show_json) {
                std::cout << insert_stmt->to_json() << std::endl;
            }
        }
    }

}  // cantor::parser
